package cn.saltice.idempotence.idem.store;

import cn.saltice.idempotence.idem.enums.IdempotentStorageTypeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @program: idempotence
 * @description: 内存级别
 * @author: Mr.Liu
 * @create: 2020-10-03 21:41
 **/
@Component("IdempotentMemoryStorage")
public class IdempotentMemoryStorage implements IdempotentStorage {
    private static final Logger logger = LoggerFactory.getLogger(IdempotentMemoryStorage.class);
    /**
     * 内部存储
     */
    private HashMap<String,IdemNode> idemMap;
    /**
     *  读写锁
     */
    private ReentrantReadWriteLock readWriteLock;
    private ReentrantReadWriteLock.ReadLock readLock;
    private ReentrantReadWriteLock.WriteLock writeLock;

    private Thread t;
    /**
     * 清理线程休眠时间 单位秒
     */
    private long sleepTime = 10L;
    {
        t = new Thread(()->{
            // 定时清除部分过期键
            while (true){
                writeLock.lock();
                try {
                    Iterator iterator = idemMap.entrySet().iterator();
                    while (iterator.hasNext()){
                        IdemNode o = (IdemNode)iterator.next();
                        long t = System.currentTimeMillis() - o.beginExpireTime;
                        if (t > o.expireTime){
                            iterator.remove();
                        }
                    }
                }catch (Exception e){
                    logger.error("定时删除出现错误");
                }finally {
                    writeLock.unlock();
                }
                try {
                    Thread.sleep(sleepTime * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.setName("MemoryStoreThread"+System.currentTimeMillis());
        t.start();
    }
    public IdempotentMemoryStorage() {
        this.idemMap = new HashMap<>();
        this.readWriteLock = new ReentrantReadWriteLock();
        this.readLock = this.readWriteLock.readLock();
        this.writeLock = this.readWriteLock.writeLock();
    }

    @Override
    public IdempotentStorageTypeEnum type() {
        return IdempotentStorageTypeEnum.MEMORY;
    }

    @Override
    public String getValue(String key) {
        readLock.lock();
        try {
            IdemNode idemNode = idemMap.get(key);
            if (idemNode == null) return null;
            long t = System.currentTimeMillis() - idemNode.beginExpireTime;
            if ( t > idemNode.expireTime) {
                // 懒删除
                idemMap.remove(key,idemNode);
                // 标记过期删除了
                idemNode.tag = false;
            }
            return idemNode.value;
        }catch (Exception e){
            logger.error("获取Value出现错误");
            return null;
        }finally {
            readLock.unlock();
        }
    }

    @Override
    public boolean setValue(String key, String value, int expireTime, TimeUnit timeUnit) {
        writeLock.lock();
        try {
            IdemNode idemNode = new IdemNode(key,value,expireTime, timeUnit);
            IdemNode o = idemMap.get(key);
            if (o == null){
                idemMap.put(key,idemNode);
                return true;
            }else{
                long t = System.currentTimeMillis() - idemNode.beginExpireTime;
                if (t > o.expireTime){
                    // 直接更新值即可
                    idemMap.put(key,idemNode);
                    return true;
                }
            }
            return false;
        }catch (Exception e){
            logger.error("设置Key-Value出现错误");
            return false;
        }finally {
            writeLock.unlock();
        }
    }


    /**
     *
     */
    static class IdemNode{
        String key;
        String value;
        /**过期时间**/
        int expireTime;

        TimeUnit timeUnit;
        /**
         * 创建时的时间戳
         */
        private long beginExpireTime;
        /**
         *  true：未过期
         *  false：过期
         */
        boolean tag;
        public IdemNode() {
        }
        public IdemNode(String key, String value, int expireTime,
                        TimeUnit timeUnit) {
            this.key = key;
            this.value = value;
            this.expireTime = expireTime;
            this.timeUnit = timeUnit;
            this.beginExpireTime = System.currentTimeMillis();
            this.tag = true;
        }
    }
}
